#ifndef CVMAT_H
#define CVMAT_H

#include <QObject>
#include <QMetaType>
#include <QScriptable>

//#include <QVector4D>
#include "opencv2/opencv.hpp"
// using namespace cv;


#include "QtCL/qclimageformat.h"


Q_DECLARE_METATYPE(QCLImageFormat)
Q_DECLARE_METATYPE(QCLImageFormat*)

Q_DECLARE_METATYPE(cv::Vec4f)
Q_DECLARE_METATYPE(cv::Vec4f*)

Q_DECLARE_METATYPE(cv::Vec4b)
Q_DECLARE_METATYPE(cv::Vec4b*)


Q_DECLARE_METATYPE(cv::Vec3b)
Q_DECLARE_METATYPE(cv::Vec3b*)



class QCLImageFormatPrototype : public QObject, public QScriptable
{
    Q_OBJECT

public:
    typedef QCLImageFormat cpp_type;

    enum ChannelOrder
    {
        Order_Invalid               = 0,
        Order_R                     = 0x10B0,
        Order_A                     = 0x10B1,
        Order_RG                    = 0x10B2,
        Order_RA                    = 0x10B3,
        Order_RGB                   = 0x10B4,
        Order_RGBA                  = 0x10B5,
        Order_BGRA                  = 0x10B6,
        Order_ARGB                  = 0x10B7,
        Order_Intensity             = 0x10B8,
        Order_Luminence             = 0x10B9,
        Order_Rx                    = 0x10BA,   // OpenCL 1.1
        Order_RGx                   = 0x10BB,   // OpenCL 1.1
        Order_RGBx                  = 0x10BC    // OpenCL 1.1
    };

    enum ChannelType
    {
        Type_Invalid                = 0,
        Type_Normalized_Int8        = 0x10D0,
        Type_Normalized_Int16       = 0x10D1,
        Type_Normalized_UInt8       = 0x10D2,
        Type_Normalized_UInt16      = 0x10D3,
        Type_Normalized_565         = 0x10D4,
        Type_Normalized_555         = 0x10D5,
        Type_Normalized_101010      = 0x10D6,
        Type_Unnormalized_Int8      = 0x10D7,
        Type_Unnormalized_Int16     = 0x10D8,
        Type_Unnormalized_Int32     = 0x10D9,
        Type_Unnormalized_UInt8     = 0x10DA,
        Type_Unnormalized_UInt16    = 0x10DB,
        Type_Unnormalized_UInt32    = 0x10DC,
        Type_Half_Float             = 0x10DD,
        Type_Float                  = 0x10DE
    };

    Q_ENUMS(ChannelOrder ChannelType)
    //Q_PROPERTY(QString text READ text WRITE setText)
    Q_PROPERTY(ChannelOrder channelOrder READ channelOrder)
    Q_PROPERTY(ChannelType channelType READ channelType)

    QCLImageFormatPrototype(QObject *parent = 0);

    ChannelOrder channelOrder() const;
    ChannelType channelType() const;
    QString enumToString(ChannelType aElement) const;
    QString enumToString(ChannelOrder aElement) const;

public slots:
    bool isNull() const;
    QString toString() const;
};

//class Vec4fPrototype : public QObject, public QScriptable
//{
//    Q_OBJECT
//
//public:
//    enum { COUNT = 4 };
//    typedef float element_type;
//    typedef cv::Vec<typename element_type, COUNT> cpp_type;
//
//    Q_PROPERTY(element_type x READ x WRITE setX)
//    Q_PROPERTY(element_type y READ y WRITE setY)
//    Q_PROPERTY(element_type z READ z WRITE setZ)
//    Q_PROPERTY(element_type w READ w WRITE setW)
//
//    Vec4fPrototype(QObject *parent = 0);
//
//    element_type x() const                  { return at(0); }
//    void setX(element_type val)             { at(0) = val;  }
//
//    element_type y() const                  { return at(1); }
//    void setY(element_type val)             { at(1) = val;  }
//
//    element_type z() const                  { return at(2); }
//    void setZ(element_type val)             { at(2) = val;  }
//
//    element_type w() const                  { return at(3); }
//    void setW(element_type val)             { at(3) = val;  }
//
//public slots:
//    QScriptValue qscript_call(float x, float y, float z, float w);
//
//    /*! element access */
//    element_type at(int i) const;
//    element_type& at(int i);
//    //QString toString() const;
//};
//
//
//class Vec4bPrototype : public QObject, public QScriptable
//{
//    Q_OBJECT
//
//public:
//    enum { COUNT = 4 };
//    typedef unsigned char element_type;
//    typedef cv::Vec<typename element_type, COUNT> cpp_type;
//
//    Q_PROPERTY(element_type x READ x WRITE setX)
//    Q_PROPERTY(element_type y READ y WRITE setY)
//    Q_PROPERTY(element_type z READ z WRITE setZ)
//    Q_PROPERTY(element_type w READ w WRITE setW)
//
//    Vec4bPrototype(QObject *parent = 0);
//
//    element_type x() const                  { return at(0); }
//    void setX(element_type val)             { at(0) = val;  }
//
//    element_type y() const                  { return at(1); }
//    void setY(element_type val)             { at(1) = val;  }
//
//    element_type z() const                  { return at(2); }
//    void setZ(element_type val)             { at(2) = val;  }
//
//    element_type w() const                  { return at(3); }
//    void setW(element_type val)             { at(3) = val;  }
//
//public slots:
//    /*! element access */
//    element_type at(int i) const;
//    element_type& at(int i);
//};
//
//class Vec3bPrototype : public QObject, public QScriptable
//{
//    Q_OBJECT
//
//public:
//    enum { COUNT = 3 };
//    typedef unsigned char element_type;
//    typedef cv::Vec<typename element_type, COUNT> cpp_type;
//
//    Q_PROPERTY(element_type x READ x WRITE setX)
//    Q_PROPERTY(element_type y READ y WRITE setY)
//    Q_PROPERTY(element_type z READ z WRITE setZ)
//
//    Vec3bPrototype(QObject *parent = 0);
//
//    element_type x() const                  { return at(0); }
//    void setX(element_type val)             { at(0) = val;  }
//
//    element_type y() const                  { return at(1); }
//    void setY(element_type val)             { at(1) = val;  }
//
//    element_type z() const                  { return at(2); }
//    void setZ(element_type val)             { at(2) = val;  }
//
//public slots:
//    /*! element access */
//    element_type at(int i) const;
//    element_type& at(int i);
//};


namespace cv
{
    typedef Vec<float, 1> Vec1f;
    typedef Vec<uchar, 1> Vec1b;
}

class CvMatrix : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QCLImageFormat imageFormat READ getImageFormat)
    Q_PROPERTY( int dims READ dims)
    Q_PROPERTY( int rows READ rows)
    Q_PROPERTY( int cols READ cols)
    Q_PROPERTY( int channels READ channels)

protected:
    cv::Mat m_mat;
    typedef cv::Vec<float, 4> (CvMatrix::*pFnAt_t)( int x, int y ) const;
    pFnAt_t m_pFnAt;

    typedef void (CvMatrix::*pFnSetAt_t)(int x, int y, const cv::Vec<float, 4> & vec);
    pFnSetAt_t m_pFnSetAt;

    static void getOptimizedFunctions(pFnAt_t & pFnAt, pFnSetAt_t & pFnSetAt, int depth, int channels);

public:
    CvMatrix(QObject *parent = 0);
    CvMatrix(const CvMatrix & m);
    CvMatrix(const cv::Mat &mat, QObject *parent = 0);

    //! constucts 2D matrix and fills it with the specified value _s.
    CvMatrix(int _rows, int _cols, int _type);
    ~CvMatrix();

    //! assignment operators
    CvMatrix& operator = (const CvMatrix& m);

    CvMatrix& operator = (const cv::Mat& m);

		operator cv::Mat &() { return m_mat; }
	operator const cv::Mat &() const { return m_mat; }

    const cv::Mat & getMat() const { return m_mat; }
    cv::Mat & getMat() { return m_mat; }
    QCLImageFormat getImageFormat() const { return getImageFormat(m_mat.depth(), m_mat.channels()); }

    static QCLImageFormat getImageFormat(int depth, int channels);
    static bool fromQCLImageFormat(int &depth, int &channels, const QCLImageFormat & imgFormat);

    //! the matrix dimensionality, >= 2
    int dims() const            { return m_mat.dims; }
    //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
    int rows() const            { return m_mat.rows; }
    int cols() const            { return m_mat.cols; }
    int channels() const        { return m_mat.channels(); }

    template<typename T, int cn, typename Tret, int cnRet>
    cv::Vec<Tret, cnRet> atType(int x, int y) const;

    template<typename T, int cn, typename Tret, int cnRet>
    void setAtType(int x, int y, const cv::Vec<Tret, cnRet> & vec);
	void emitModified() { emit modified(*this); }

public slots:

    QString toString() const;
    ////! returns true if matrix data is NULL
    bool empty() const          { return m_mat.empty(); }
    //! returns the total number of matrix elements
    uint total() const          { return (uint) m_mat.total(); }


    //! returns true iff the matrix data is continuous
    // (i.e. when there are no gaps between successive rows).
    // similar to CV_IS_MAT_CONT(cvmat->type)
    bool isContinuous() const   { return m_mat.isContinuous(); }
    
    //! returns true if the matrix is a submatrix of another matrix
    bool isSubmatrix() const    { return m_mat.isSubmatrix(); }
    
    //! returns element size in bytes,
    // similar to CV_ELEM_SIZE(cvmat->type)
    uint elemSize() const       { return (uint) m_mat.elemSize(); }
    //! returns the size of element channel in bytes.
    uint elemSize1() const      { return (uint) m_mat.elemSize1(); }


    cv::Vec4f atDynamic(int x, int y) const;
    cv::Vec4f at(int x, int y) const;
    float atChannel(int x, int y, int ch) const { return at(x, y)[ch]; }
    void setAt(int x, int y, const cv::Vec4f & vec4);

    //cv::Vec4f getFromVec4fAt(int x, int y) const           { return m_mat.at<cv::Vec4f>(x, y); }
    //void setToVec4fAt(int x, int y, const cv::Vec4f & vec) { m_mat.at<cv::Vec4f>(x, y) = vec; }
    //cv::Vec3b getFromVec3bAt(int x, int y) const           { return m_mat.at<cv::Vec3b>(x, y); }
    //void setToVec3bAt(int x, int y, const cv::Vec3b & vec) { m_mat.at<cv::Vec3b>(x, y) = vec; }

signals:
    void modified(const CvMatrix & matrix);
};

QScriptValue ctorVec4fNew(QScriptContext *context, QScriptEngine *engine);
QScriptValue ctorVec4f(QScriptContext *context, QScriptEngine *engine);
QScriptValue toScriptValueVec4f(QScriptEngine *engine, const cv::Vec4f &s);
void fromScriptValueVec4f(const QScriptValue &obj, cv::Vec4f &s);
QScriptValue toStringVec4(QScriptContext *context, QScriptEngine *engine);
QScriptValue ctorQCLImageFormat(QScriptContext *context, QScriptEngine *engine);

#endif // CVMAT_H

